/*
 *  Copyright (C) 2020 original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License
 *  is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 *  or implied. See the License for the specific language governing permissions and limitations under
 *  the License.
 */

package vip.justlive.easyproxy.core.context;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import vip.justlive.easyproxy.core.protocol.ProxyData;
import vip.justlive.oxygen.core.net.aio.core.ChannelContext;

/**
 * 远程连接
 *
 * @author wubo
 */
@Data
@Slf4j
public class RemoteContext implements Serializable {

  private static final long serialVersionUID = -7749823091794496330L;
  private static final Map<String, RemoteContext> CONTEXTS = new HashMap<>(4);

  private final String key;
  private String address;
  private List<ServerProxyContext> proxies = new ArrayList<>();

  private transient ChannelContext channel;

  public synchronized static void register(String key, ChannelContext channel) {
    log.info("register remote for {} {}", key, channel);
    RemoteContext ctx = CONTEXTS.get(key);
    if (ctx != null) {
      ctx.channel.write(ProxyData.close());
    } else {
      ctx = new RemoteContext(key);
      CONTEXTS.put(key, ctx);
    }
    ctx.channel = channel;
    ctx.address = channel.getAddress().toString();
    channel.addAttr(RemoteContext.class.getName(), ctx);
  }

  public static RemoteContext lookup(String key) {
    return CONTEXTS.get(key);
  }

  public static RemoteContext lookup(ChannelContext channel) {
    return (RemoteContext) channel.getAttrs().get(RemoteContext.class.getName());
  }

  public synchronized static void remove(ChannelContext channel) {
    RemoteContext ctx = (RemoteContext) channel.getAttrs().get(RemoteContext.class.getName());
    if (ctx == null) {
      return;
    }
    log.info("remove remote for {} {}", ctx.key, channel);
    RemoteContext exist = CONTEXTS.get(ctx.key);
    if (ctx == exist && ctx.channel == channel) {
      CONTEXTS.remove(ctx.key);
    }
    for (ServerProxyContext proxy : ctx.proxies) {
      proxy.getServer().stop();
    }
    ctx.proxies.clear();
  }

  public ServerProxyContext find(int id) {
    for (ServerProxyContext proxy : proxies) {
      if (proxy.getId() == id) {
        return proxy;
      }
    }
    return null;
  }

  public ServerProxyContext remove(int id) {
    Iterator<ServerProxyContext> it = proxies.iterator();
    while (it.hasNext()) {
      ServerProxyContext proxy = it.next();
      if (proxy.getId() == id) {
        it.remove();
        return proxy;
      }
    }
    return null;
  }
}
